#!/usr/bin/env python

import pygtk
import gtk
import gobject
import webkit
import time
import re
import gettext

# for the 'save' thread
import thread

# from youtube-dl
import httplib
import socket
import time
import urllib2

_ = gettext.gettext

VERSION = "0.2.41"

class WebKitTest:
    
    def __init__(self):

        gobject.threads_init()
        
        video_id = 'http://www.youtube.com/v/wKaK31X7xAs'

        conversation = None

        view = webkit.WebView()
        view.open(video_id)

        scrolled_window = gtk.ScrolledWindow()
        scrolled_window.add(view)
        scrolled_window.set_size_request(400,300)

        vbox = gtk.VBox(False, 0)

        dbutton = gtk.Button(None, gtk.STOCK_SAVE)
        dbutton.set_size_request(400,30)
        dbutton.connect('clicked', self.downloadVideo, video_id, conversation)

        vbox.pack_start(scrolled_window,True)

        vbox.pack_end(dbutton,False)

        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        #window.set_size_request(400,300)
        window.connect("destroy", gtk.main_quit)

        window.add(vbox)

        window.set_title('Emesene - YAYoutube')
        window.show_all()

    def downloadVideo(self, button, video, conversation):
        youtube_dl = Youtube_dl()
        youtube_dl.startDownload(video, conversation)

# From here all youtube-dl code. It's identical to the code
# in yayoutube, perfect to test non working videos

# constants from youtube-dl
const_1k = 1024
const_initial_block_size = 10 * const_1k
const_epsilon = 0.0001
const_timeout = 120

const_video_url_str = 'http://www.youtube.com/watch?v=%s'
const_video_url_re = re.compile(r'^((?:http://)?(?:\w+\.)?youtube\.com/(?:v/|(?:watch(?:\.php)?)?\?(?:.+&)?v=))?([0-9A-Za-z_-]+)(?(1)[&/].*)?$')
const_login_url_str = 'http://www.youtube.com/login?next=/watch%%3Fv%%3D%s'
const_login_post_str = 'current_form=loginForm&next=%%2Fwatch%%3Fv%%3D%s&username=%s&password=%s&action_login=Log+In'
const_age_url_str = 'http://www.youtube.com/verify_age?next_url=/watch%%3Fv%%3D%s'
const_age_post_str = 'next_url=%%2Fwatch%%3Fv%%3D%s&action_confirm=Confirm'
const_url_t_param_re = re.compile(r', "t": "([^"]+)"')
const_video_url_real_str = 'http://www.youtube.com/get_video?video_id=%s&t=%s'
const_video_title_re = re.compile(r'<title>YouTube - ([^<]*)</title>', re.M | re.I)

class Youtube_dl:

    def startDownload (self, video, conversation):
        thread.start_new_thread(self.downloadTheVideo,(video, conversation))
    
    # Generic extract step
    def youtube_dl_extract_step(self, step_title, step_error, regexp, data):
        match = regexp.search(data)

        if match is None:
            return None

        extracted_data = match.group(1)
        return extracted_data

    # Generic download step
    def youtube_dl_download_step(self, return_data_flag, step_title, step_error, url, post_data=None):
        try:
            data = self.youtube_dl_perform_request(url, post_data).read()
            if return_data_flag:
                return data
            return None

        except (urllib2.URLError, ValueError, httplib.HTTPException, TypeError, socket.error):
            return None

    # Wrapper to create custom requests with typical headers
    def youtube_dl_request_create(self, url, data=None):
        retval = urllib2.Request(url)
        if data is not None:
            retval.add_data(data)
        # Try to mimic Firefox, at least a little bit
        retval.add_header('User-Agent', 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11')
        retval.add_header('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7')
        retval.add_header('Accept', 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5')
        retval.add_header('Accept-Language', 'en-us,en;q=0.5')
        return retval

    # Perform a request, process headers and return response
    def youtube_dl_perform_request(self, url, data=None):
        request = self.youtube_dl_request_create(url, data)
        response = urllib2.urlopen(request)
        return response

    # Calculate new block size based on previous block size
    def youtube_dl_new_block_size(self, before, after, bytes):
        new_min = max(bytes / 2.0, 1.0)
        new_max = max(bytes * 2.0, 1.0)
        dif = after - before
        if dif < const_epsilon:
            return int(new_max)
        rate = bytes / dif
        if rate > new_max:
            return int(new_max)
        if rate < new_min:
            return int(new_min)
        return int(rate)
    
    def showMessage(self, conv, msg, type='error'):
        try:
            conv.appendOutputText(None, msg, type)
        except:
            print msg
            # pass

    # Main function that downloads the video and stores it
    def downloadTheVideo(self, *args):
        
        # 'conv', args[0] #for sending info message
        # 'id', args[1] #for downloading video

        # Set socket timeout
        socket.setdefaulttimeout(const_timeout)
        
        # Get video URL
        video_url_cmdl = args[0]
        conv = args[1]

        # Verify video URL format and convert to "standard" format
        video_url_mo = const_video_url_re.match(video_url_cmdl)
        if video_url_mo is None:
            # Error: URL does not seem to be a youtube video URL. If it is, report a bug.
            self.showMessage(conv, _('Video download failed'))
            return

        video_url_id = video_url_mo.group(2)
        video_url = const_video_url_str % video_url_id

        # Save Dialog

        global file
        dialog = gtk.FileChooserDialog(_("Save video as.."),
                                       None,
                                       gtk.FILE_CHOOSER_ACTION_SAVE,
                                       (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                       gtk.STOCK_SAVE, gtk.RESPONSE_OK))
        dialog.set_default_response(gtk.RESPONSE_OK)

        filter = gtk.FileFilter()
        filter.set_name(_("Flash Videos"))
        filter.add_pattern("*.flv")

        dialog.add_filter(filter)
        dialog.show()
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            video_filename = dialog.get_filename()
            dialog.destroy()
        else:	
            dialog.destroy()
            self.showMessage(conv, _('Video download canceled'))
            return

        # Check if ends with '.flv'... add it or not
        nametemp =  video_filename.lower() #can't be persistent 'cause *nix is case sensitive
        if nametemp.endswith('.flv') == False:
            video_filename = video_filename + '.flv'

        t_start = time.strftime("%H:%M:%S", time.localtime())
        self.showMessage(conv, _('Video download started [%s]' % t_start),'information')

        # Retrieve video webpage
        video_webpage = self.youtube_dl_download_step(True, 'Retrieving video webpage', 'unable to retrieve video webpage', video_url)
        if video_webpage == None:
            self.showMessage(conv, _('Video download failed'))
            return
        
        # Extract needed video URL parameters
        video_url_t_param = self.youtube_dl_extract_step('Extracting URL "t" parameter', 'unable to extract URL "t" parameter', const_url_t_param_re, video_webpage)
        if video_url_t_param == None:
            self.showMessage(conv, _('Video download failed'))
            return

        video_url_real = const_video_url_real_str % (video_url_id, video_url_t_param)
        
        # Retrieve video data
        try:
            video_data = self.youtube_dl_perform_request(video_url_real)

            try:
                video_file = open(video_filename, 'wb')
            except (IOError, OSError):
                # sys.exit('Error: unable to open "%s" for writing.' % video_filename)
                self.showMessage(_('Video download failed'))
                return

            byte_counter = 0
            block_size = const_initial_block_size
            while True:
                before = time.time()
                video_block = video_data.read(block_size)
                after = time.time()
                dl_bytes = len(video_block)
                if dl_bytes == 0:
                    break
                byte_counter += dl_bytes
                video_file.write(video_block)
                block_size = self.youtube_dl_new_block_size(before, after, dl_bytes)

            video_file.close()

            t_end = time.strftime("%H:%M:%S", time.localtime())
            self.showMessage(conv, _('Video saved in %s [%s]') % (video_filename, t_end),'information')

        except (urllib2.URLError, ValueError, httplib.HTTPException, TypeError, socket.error):
            self.showMessage(conv, _('Video download failed'))
            return
    


gtk.gdk.threads_init()
gtk.gdk.threads_enter()
test = WebKitTest()
gtk.main()
gtk.gdk.threads_leave()

